// file：arch/x86/arch/cpu/interrupt
// autor:jiangxinpeng
// time:2021.2.3
// copyright:(C) 2020-2050 by Jiang xinpeng. All rights reserved.

#include <arch/interrupt.h>
#include <arch/x86.h>
#include <arch/segment.h>
#include <arch/page.h>

#include <lib/stddef.h>
#include <lib/string.h>

#include <os/syscall.h>
#include <os/task.h>
#include <os/exception.h>
#include <os/schedule.h>
#include <os/hardirq.h>
#include <os/debug.h>

interrupt_t interrupt[INTERRUPT_NUM_MAX];

void InterruptGeneralHandler(uint32_t esp)
{
    trap_frame_t *frame = (trap_frame_t *)esp;

    KPrint("[sys] exception occur! error code %x\n", frame->vec_num);

    // IRQ7 and IRQ15 may be generate spurious interrupt,no need deal
    if (frame->vec_num == 0x27 || frame->vec_num == 0x2f)
        return;

    // only task init done can interrupt
    if (task_init_done)
    {
        //output current task info
        if(cur_task!=NULL)
        {
            KPrint("current task info name: %s pid %d ppid\n",cur_task->name,cur_task->pid,cur_task->parent_pid);
        }

        switch (frame->vec_num)
        {
        case EP_PAGE_FAULT:
            PageDoFault(frame);
            break;
        case EP_NO_DEVICE_AVAILABLE:
            KPrint("[sys] device no available\n");
            ExceptionForceSelf(EXC_CODE_DEVICE);
            break;
        case EP_COPROCESSOR_SEGMENT_BOUND:
        case EP_X87_FLOATPOINT:
        case EP_SIMD_FLOATPOINT:
            KPrint("[sys] FPE error\n");
            ExceptionForceSelf(EXC_CODE_FPE);
            break;
        case EP_OVERFLOW:
        case EP_BOUND_RANGE:
        case EP_INVALID_TSS:
        case EP_ALIGN_CHECK:
            KPrint("[sys] bus error\n");
            ExceptionForceSelf(EXC_CODE_BUS);
            break;
        case EP_SEGMENT_NOT_PRESENT:
        case EP_GENERAL_PROTECT:
            KPrint("[sys] segment fault\n");
            ExceptionForceSelf(EXC_CODE_SEGV);
            break;
        case EP_STACK_ERROR:
            KPrint("[sys] stack error\n");
            ExceptionForceSelf(EXC_CODE_STKFLT);
            break;
        case EP_MACHINE_CHECK:
        case EP_NMI:
            KPrint("[sys] nmi\n");
            ExceptionForceSelf(EXC_CODE_INT);
            break;
        case EP_DIV:
        case EP_INVALID_OPCODE:
            KPrint("[sys] illige operator\n");
            ExceptionForceSelf(EXC_CODE_ILL);
            break;
        case EP_DOUBLE_ERROR:
            KPrint("[sys] double err\n");
            ExceptionForceSelf(EXC_CODE_FINALHIT);
        case EP_DEBUG:
        case EP_BREAKPOINT:
            KPrint("[sys] debug point\n");
            ExceptionForceSelf(EXC_CODE_TRAP);
            break;
        default:
            Panic("[sys] unknown exception occured\n");
            break;
        }
    }
    else
    {
        switch (frame->vec_num)
        {
        case EP_PAGE_FAULT:
        {
            uint64_t addr = ReadCR2();
            KPrint("page fault addr:%x\n", addr);
            break;
        }
        case EP_NO_DEVICE_AVAILABLE:
        case EP_COPROCESSOR_SEGMENT_BOUND:
        case EP_X87_FLOATPOINT:
        case EP_SIMD_FLOATPOINT:
        case EP_OVERFLOW:
        case EP_BOUND_RANGE:
        case EP_INVALID_TSS:
        case EP_ALIGN_CHECK:
        case EP_SEGMENT_NOT_PRESENT:
        case EP_GENERAL_PROTECT:
        case EP_STACK_ERROR:
        case EP_MACHINE_CHECK:
        case EP_NMI:
        case EP_DIV:
        case EP_INVALID_OPCODE:
        case EP_DOUBLE_ERROR:
        case EP_DEBUG:
        case EP_BREAKPOINT:
        default:
            KPrint("[exception] task no init,can't cope with! error: %x\n", frame->vec_num);
            break;
        }
        DumpTrapFrame(frame);
        Panic("Exception no resuloved!\n");
    }
}

// exception init
void InterruptExceptionInit()
{
    int i;

    interrupt[EP_DIV].name = "#DE divid error";
    interrupt[EP_DEBUG].name = "#DB debug exception";
    interrupt[EP_NMI].name = "NMI interrupt";
    interrupt[EP_BREAKPOINT].name = "#BP breakpoint exception";
    interrupt[EP_OVERFLOW].name = "#OF overflow exception";
    interrupt[EP_BOUND_RANGE].name = "#BR BOUND range exception";
    interrupt[EP_INVALID_OPCODE].name = "#UD undefine opcode exception";
    interrupt[EP_NO_DEVICE_AVAILABLE].name = "#NM device no available exception";
    interrupt[EP_DOUBLE_ERROR].name = "#DE double fault";
    interrupt[EP_COPROCESSOR_SEGMENT_BOUND].name = "#MF coprocessor segment overflow";
    interrupt[EP_INVALID_TSS].name = "#TS invalid tss";
    interrupt[EP_SEGMENT_NOT_PRESENT].name = "#NP segment no present";
    interrupt[EP_STACK_ERROR].name = "#SS stack segment fault";
    interrupt[EP_GENERAL_PROTECT].name = "#GP general protect";
    interrupt[EP_PAGE_FAULT].name = "#PF page fault";
    interrupt[EP_RESERVED0].name = "reserved";
    interrupt[EP_X87_FLOATPOINT].name = "#MF x87 fpu error";
    interrupt[EP_ALIGN_CHECK].name = "#AC alignment check exception";
    interrupt[EP_MACHINE_CHECK].name = "#MC machine check exception";
}

void InterruptInit()
{
    int i;

    // init interrupt info
    for (i = 0; i < INTERRUPT_NUM_MAX; i++)
    {
        interrupt[i].name = "UNKNOW";
        interrupt[i].function = InterruptGeneralHandler;
    }
    // init exception name
    InterruptExceptionInit();
}

void InterruptRegister(uint32_t interrupt_index, interrupt_handler_t function)
{
    interrupt[interrupt_index].function = function;
}

void InterruptUnRegister(uint32_t interrupt_index)
{
    interrupt[interrupt_index].function = InterruptGeneralHandler;
}

void IrqRegisterInterrupt(uint32_t irq, interrupt_handler_t function)
{
    // master chips
    if (irq > 0 && irq < 8)
    {
        interrupt[IRQOFF1 + irq].function = function;
    }
    else
    {
        if (irq > 8 && irq < 16)
            interrupt[IRQOFF2 + (irq - 8)].function = function;
    }
}

void IrqUnRegisterInterrupt(uint32_t irq)
{
    // master chips
    if (irq > 0 && irq < 8)
    {
        interrupt[IRQOFF1 + irq].function = InterruptGeneralHandler;
    }
    else
    {
        if (irq > 8 && irq < 16)
            interrupt[IRQOFF2 + (irq - 8)].function = InterruptGeneralHandler;
    }
}

void ExceptionFrameBuild(uint32_t code, exception_handler_t handler, trap_frame_t *frame)
{
    // disable interrupt
    InterruptDisable();

    // set exception frame
    exception_frame_t *exc_frame = (exception_frame_t *)((frame->esp - sizeof(exception_frame_t)) & -8UL);
    // copy trap frame to exception frame
    memcpy(&exc_frame->trap_frame, frame, sizeof(trap_frame_t));
    // set error code
    frame->error_code = code;
    // set exception frame exception code
    exc_frame->code = code;
    // exception return address point return code
    exc_frame->return_addr = exc_frame->return_code;
    // create except return code return from user to kernel
    // mov eax,SYS_EXCRET
    // int 0x40
    exc_frame->return_code[0] = 0xb8; // mov eax,data instruct code is 0xb8
    *(uint32_t *)(exc_frame->return_code + 1) = SYS_EXCRET;
    *(uint16_t *)(exc_frame->return_code + 5) = 0x40cd; // int instruction code is 0xcd,interrupt code is 0x40

    // set frame eip to exception handler
    frame->eip = (uint32_t)handler;
    frame->esp = (uint32_t)exc_frame;
    // set frame all segment register to kernel segment
    frame->ds = frame->es = frame->fs = frame->gs = SEG_SEL_USER_DATA;
    frame->cs = SEG_SEL_USER_CODE;
    frame->ss = SEG_SEL_USER_STACK;

    // enable interrupt
    InterruptEnable();
}

// exception return
// return exception frame eax register
int ExceptionReturn(trap_frame_t *frame)
{
    // get exc_frame
    exception_frame_t *exc_frame = (exception_frame_t *)(frame->esp);
    exception_manager_t *manager = &(cur_task->exception_manager);

    SpinLockDisInterrupt(&manager->manager_lock);
    manager->user_mode = 0;
    // copy exc_frame source trap frame to frame,recover trap frame
    memcpy(frame, &exc_frame->trap_frame, sizeof(trap_frame_t));
    SpinUnlockEnInterrupt(&manager->manager_lock);

    return frame->eax;
}

void DumpTrapFrame(trap_frame_t *frame)
{
    // dump tram frame
    KPrint(PRINT_DEBUG "trap frame:\n");
    KPrint("vector: %d %s\n", frame->vec_num, interrupt[frame->vec_num].name);
    KPrint(PRINT_DEBUG "edi:%x esi:%x ebp:%x edx:%x ecx:%x ebx:%x eax:%x\n",
           frame->edi, frame->esi, frame->ebp, frame->edx, frame->ecx, frame->ebx, frame->eax);
    KPrint(PRINT_DEBUG "cs:%x ds:%x es:%x ss:%x fs:%x gs:%x error code:%x eip:%x esp:%x eflags:%x\n",
           frame->cs, frame->ds, frame->es, frame->ss, frame->fs, frame->gs, frame->error_code, frame->eip, frame->esp, frame->eflags);

    if (frame->error_code != 0xFFFFFFFF)
    {
        if (frame->error_code & ERROR_CODE_EXT)
        {
            KPrint(PRINT_DEBUG "External Event: NMI ,hardware Interrupt\n");
        }
        else
        {
            KPrint(PRINT_DEBUG "No External Event: inside\n");
        }
        if (frame->error_code & ERROR_CODE_IDT)
        {
            KPrint(PRINT_DEBUG "IDT: sel in idt\n");
        }
        else
        {
            KPrint(PRINT_DEBUG "IDT: sel in gdt or ldt\n");
        }
        if (frame->error_code & ERROR_CODE_TI)
        {
            KPrint(PRINT_DEBUG "TI: sel in ldt\n");
        }
        else
        {
            KPrint(PRINT_DEBUG "TI: sel in gdt\n");
        }
    }
}
